//
//  CrashMonitor.swift
//  app_monitor
//
//  Created by Longxiang Guo (QXQ4633) on 2021/12/24.
//

import Foundation

class CrashMonitor: AppMonitorProtocol {
    
    static var isARM64: Bool {
        return cpu_name().contains("ARM")
    }
    static let instance = CrashMonitor()
    func start() {
        NSSetUncaughtExceptionHandler { exception in
            let stackSymbols = Thread.callStackSymbols.joined(separator: "\n")
            CrashMonitor.instance.valueChanged?(stackSymbols)
        }
        signal(SIGKILL, handleSignal)
        signal(SIGABRT, handleSignal)
        signal(SIGILL, handleSignal)
        signal(SIGSEGV, handleSignal)
        signal(SIGFPE, handleSignal)
        signal(SIGBUS, handleSignal)
        signal(SIGPIPE, handleSignal)
    }
    
    func stop() {
        NSSetUncaughtExceptionHandler(nil)
    }
    
    func flushRecords() {
   
    }
    
    var valueChanged: ValueChanged?
    
}

 

func handleSignal(signal: Int32){
    //TODO: paser the crash info
    /*
     public var __es: __darwin_x86_exception_state64

     public var __ss: __darwin_x86_thread_state64

     public var __fs: __darwin_x86_float_state64
     */
    
    // var context: mcontext_t
    // var count: mach_msg_type_number_t
    // if (CrashMonitor.isARM64){
    //     count = mach_msg_type_number_t(MemoryLayout<x86_thread_full_state64_t>.stride/MemoryLayout<integer_t>.stride)
    //     let thread = ksself_thread()
 
      
    // }
    
    //thread_suspend(T##target_act: thread_read_t##thread_read_t)
 
    // guard let exeptionHandler = NSGetUncaughtExceptionHandler() else { return }
    // exeptionHandler(NSException(name: NSExceptionName(rawValue: "\(signal)"), reason: "unknown", userInfo: [:]))
}

func ksself_thread() -> thread_read_t{
    var thread_self = mach_thread_self()
    mach_port_deallocate(mach_task_self_, thread_self)
    return thread_read_t(thread_self)
}

func kscpu_i_fillState(thread: thread_read_t,
                        state: thread_state_t,
                    flavor: thread_state_flavor_t ,
                       stateCount: mach_msg_type_number_t ) -> Bool
{
    print("Filling thread state with flavor %x.", flavor);
    var stateCountBuff = stateCount;
    let kr:kern_return_t;
    
    kr = thread_get_state(thread, flavor, state, &stateCountBuff);
    if(kr != KERN_SUCCESS)
    {
        print("thread_get_state: %s", mach_error_string(kr) ?? "");
        return false;
    }
    return true;
}
 


fileprivate func cpu_name() -> String {
        
        let HOST_BASIC_INFO_COUNT = MemoryLayout<host_basic_info>.stride/MemoryLayout<integer_t>.stride
        var size = mach_msg_type_number_t(HOST_BASIC_INFO_COUNT)
        var hostInfo = host_basic_info()
        let result = withUnsafeMutablePointer(to: &hostInfo) {
            $0.withMemoryRebound(to: integer_t.self, capacity:Int(size)){
                host_info(mach_host_self(), Int32(HOST_BASIC_INFO), $0, &size)
            }
        }
        print(result, hostInfo)
        switch hostInfo.cpu_type {
        case CPU_TYPE_ARM:
            return "CPU_TYPE_ARM"
        case CPU_TYPE_ARM64:
            return "CPU_TYPE_ARM64"
        case CPU_TYPE_ARM64_32:
            return"CPU_TYPE_ARM64_32"
        case CPU_TYPE_X86:
            return "CPU_TYPE_X86"
        case CPU_TYPE_X86_64:
            return"CPU_TYPE_X86_64"
        case CPU_TYPE_ANY:
            return"CPU_TYPE_ANY"
        case CPU_TYPE_VAX:
            return"CPU_TYPE_VAX"
        case CPU_TYPE_MC680x0:
            return"CPU_TYPE_MC680x0"
        case CPU_TYPE_I386:
            return"CPU_TYPE_I386"
        case CPU_TYPE_MC98000:
            return"CPU_TYPE_MC98000"
        case CPU_TYPE_HPPA:
            return"CPU_TYPE_HPPA"
        case CPU_TYPE_MC88000:
            return"CPU_TYPE_MC88000"
        case CPU_TYPE_SPARC:
            return"CPU_TYPE_SPARC"
        case CPU_TYPE_I860:
            return"CPU_TYPE_I860"
        case CPU_TYPE_POWERPC:
            return"CPU_TYPE_POWERPC"
        case CPU_TYPE_POWERPC64:
            return"CPU_TYPE_POWERPC64"
        default:
            return ""
        }
    }
